module dwt.extra.fileversion;


version(build)
{
	pragma(link, "version_dwt.lib");
}

extern(Windows)
{

	private import std.c.windows.windows;
	
	struct VS_FIXEDFILEINFO { 
		DWORD dwSignature; 
		DWORD dwStrucVersion; 
		DWORD dwFileVersionMS; 
		DWORD dwFileVersionLS; 
		DWORD dwProductVersionMS; 
		DWORD dwProductVersionLS; 
		DWORD dwFileFlagsMask; 
		DWORD dwFileFlags; 
		DWORD dwFileOS; 
		DWORD dwFileType; 
		DWORD dwFileSubtype; 
		DWORD dwFileDateMS; 
		DWORD dwFileDateLS; 
	};
	
version(ANSI){
	BOOL VerQueryValueA(LPVOID pBlock, LPSTR lpSubBlock, PUINT lplpBuffer, PUINT puLen);
	DWORD GetFileVersionInfoSizeA(LPSTR lptstrFilename, LPDWORD lpdwHandle);
	BOOL GetFileVersionInfoA(LPSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData);
	alias VerQueryValueA VerQueryValue;
	alias GetFileVersionInfoSizeA GetFileVersionInfoSize;
	alias GetFileVersionInfoA GetFileVersionInfo;
}else {
	BOOL VerQueryValueW(LPVOID pBlock, LPWSTR lpSubBlock, PUINT lplpBuffer, PUINT puLen);
	DWORD GetFileVersionInfoSizeW(LPWSTR lptstrFilename, LPDWORD lpdwHandle);
	BOOL GetFileVersionInfoW(LPWSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData);
	alias VerQueryValueW VerQueryValue;
	alias GetFileVersionInfoSizeW GetFileVersionInfoSize;
	alias GetFileVersionInfoW GetFileVersionInfo;
}

}



auto class FileVersion
{	
	private import dwt.dwt;
	private import dwt.internal.win32.os;
	
	
	ubyte[]	pBlock;
	uint   	dwLangCharset; 
	
	~this()
	{
		pBlock = null;	
	}
	
	
	char[] getFileDescription()  {return queryValue("FileDescription"); }
	
	char[] getFileVersion()      {return queryValue("FileVersion");     }
	
	char[] getInternalName()     {return queryValue("InternalName");    }
	
	char[] getCompanyName()      {return queryValue("CompanyName");     }
	 
	char[] getLegalCopyright()   {return queryValue("LegalCopyright");  }
	
	char[] getOriginalFilename() {return queryValue("OriginalFilename");}
	
	char[] getProductName()      {return queryValue("ProductName");     }
	
	char[] getProductVersion()   {return queryValue("ProductVersion");  }
   	
   	char[] getFixedFileVersion()
	{
	    char[] strVersion = "";
		VS_FIXEDFILEINFO vsffi;
	
	    if ( getFixedInfo(&vsffi) )
	    {
	    	strVersion = std.string.format("%d.%d.%d.%d", HIWORD(vsffi.dwFileVersionMS),
	    		LOWORD(vsffi.dwFileVersionMS), HIWORD(vsffi.dwFileVersionLS), LOWORD(vsffi.dwFileVersionLS));
	    }
	    return strVersion;
	}
	
	boolean getFixedInfo(VS_FIXEDFILEINFO* vsffi)
	{
	    // Must call Open() first
	    assert(pBlock !is null);
	    if ( pBlock is null)
	        return false;
	
	    UINT nQuerySize;
	    VS_FIXEDFILEINFO* pVsffi;
	    TCHAR subBlock[] = new TCHAR[2];
	    subBlock[0] = '\\';
	   	subBlock[1] = 0; 
	    if ( VerQueryValue(pBlock.ptr, subBlock.ptr, cast(PUINT)&pVsffi, &nQuerySize) )
	    {
	    	*vsffi = *pVsffi;
	        return true;
	    }
	
	    return false;
	}
	
	boolean open(char[] moduleName)
	{
		assert(moduleName);
	    assert(pBlock is null);
	
	    // Get the version information size for allocate the buffer
	    DWORD dwHandle;     
	    TCHAR* szName = Converter.StrToTCHARz(moduleName);
	    DWORD dwDataSize = GetFileVersionInfoSize(szName, &dwHandle); 
	    if ( dwDataSize == 0 ) 
	        return false;
	
	    // Allocate buffer and retrieve version information
	    pBlock = new ubyte[dwDataSize]; 
	    if (!GetFileVersionInfo(szName, dwHandle, dwDataSize, pBlock.ptr) )
	    {
	        pBlock = null;
	        return false;
	    }
	
	    // Retrieve the first language and character-set identifier
	    UINT cbTranslate;
	    DWORD *lpTranslate;
	    if (!VerQueryValue(pBlock.ptr, "\\VarFileInfo\\Translation\0",
	                         cast(PUINT)&lpTranslate, &cbTranslate) )
	    {
	        pBlock = null;
	        return false;
	    }
	
	    // Swap the words to have lang-charset in the correct format
	    dwLangCharset = LOWORD(*lpTranslate) << 16 | HIWORD(*lpTranslate);
	
	    return true;
	}
	
	char[] queryValue(char[] valueName, uint langCharset = 0)
	{
		if(pBlock is null)	return "";
		
		if ( langCharset == 0 )
        	langCharset = dwLangCharset;
        	
        // Query version information value
		UINT nQuerySize;
		TCHAR* lpData;
		char[] strValue, strBlockName;
		strBlockName = std.string.format("\\StringFileInfo\\%.08x\\%s", langCharset, valueName);
		
		if (VerQueryValue(pBlock.ptr, Converter.StrToTCHARz(strBlockName), cast(PUINT)&lpData, &nQuerySize) )
		    strValue = Converter.TCHARzToStr(lpData, -1);
		
		return strValue;
	}
}